package vip.liux.backend.infrastructure.urls;

import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import vip.liux.multitenancy.ICurrentTenant;
import vip.liux.multitenancy.configuration.TenantConfiguration;
import vip.liux.multitenancy.store.ITenantStore;

import java.util.Objects;

/**
 * 应用URL提供者实现类，负责根据应用名称和URL名称获取具体的URL地址，
 * 并自动替换租户相关占位符，支持重定向URL白名单校验。
 */
@Component
public class AppUrlProvider implements IAppUrlProvider {

    /**
     * 租户ID占位符
     */
    public static final String TENANT_ID_PLACEHOLDER = "{{tenantId}}";
    /**
     * 租户名称占位符
     */
    public static final String TENANT_NAME_PLACEHOLDER = "{{tenantName}}";

    private static final Logger log = LoggerFactory.getLogger(AppUrlProvider.class);

    /**
     * 应用URL配置属性
     */
    protected final AppUrlConfiguration appUrlConfiguration;
    /**
     * 当前租户信息
     */
    protected final ICurrentTenant currentTenant;
    /**
     * 租户信息存储服务
     */
    protected final ITenantStore tenantStore;

    /**
     * 构造方法，注入应用URL配置、当前租户、租户存储服务
     *
     * @param appUrlConfiguration 应用URL配置属性
     * @param currentTenant    当前租户
     * @param tenantStore      租户信息存储服务
     */
    public AppUrlProvider(AppUrlConfiguration appUrlConfiguration, ICurrentTenant currentTenant, ITenantStore tenantStore) {
        this.appUrlConfiguration = appUrlConfiguration;
        this.currentTenant = currentTenant;
        this.tenantStore = tenantStore;
    }

    /**
     * 获取指定应用及指定URL名称的完整URL，并自动替换租户相关占位符
     *
     * @param appName 应用名称
     * @param urlName URL名称（可选，若为空则取根URL）
     * @return 替换租户信息后的URL
     */
    @Override
    public String getUrl(@NotNull String appName, @Nullable String urlName) {
        String configuredUrl = getConfiguredUrl(appName, urlName);
        return replacePlaceholders(configuredUrl);
    }

    /**
     * 校验重定向目标URL是否在允许白名单中
     *
     * @param url 待校验的URL
     * @return 是否允许重定向
     */
    @Override
    public boolean isRedirectAllowedUrl(String url) {
        boolean allow = appUrlConfiguration.getRedirectAllowedUrls().stream()
                .anyMatch(allowedUrl -> StringUtils.startsWithIgnoreCase(url, allowedUrl));
        if (!allow) {
            log.error("Invalid RedirectUrl: {}, 请在 {} 中配置重定向白名单!", url, AppUrlConfiguration.class.getSimpleName());
        }
        return allow;
    }

    /**
     * 获取指定应用及URL名称的配置URL，未配置时抛出详细异常
     *
     * @param appName 应用名称
     * @param urlName URL名称（可选）
     * @return 配置的URL
     */
    protected String getConfiguredUrl(String appName, String urlName) {
        String url = getUrlOrNull(appName, urlName);
        if (StringUtils.isNotEmpty(url)) {
            return url;
        }
        if (StringUtils.isNotEmpty(urlName)) {
            throw new RuntimeException(String.format(
                    "未配置应用 '%s' 的Url '%s'，请在 %s 中进行配置！", appName, urlName, AppUrlConfiguration.class.getSimpleName()));
        }
        throw new RuntimeException(String.format(
                "未配置应用 '%s' 的根Url，请在 %s 中进行配置！", appName, AppUrlConfiguration.class.getSimpleName()));
    }

    /**
     * 替换URL中的租户相关占位符
     *
     * @param url 原始URL
     * @return 替换后的URL
     */
    protected String replacePlaceholders(String url) {
        // 替换租户ID占位符
        url = url.replace(TENANT_ID_PLACEHOLDER,
                Objects.toString(currentTenant.getId(), ""));
        // 替换租户名称占位符
        if (url.contains(TENANT_NAME_PLACEHOLDER)) {
            String tenantName = getCurrentTenantName();
            url = url.replace(TENANT_NAME_PLACEHOLDER, Objects.toString(tenantName, ""));
        }
        return url;
    }

    /**
     * 获取当前租户名称，优先取当前租户对象中的名称，若无则通过租户存储服务查找
     *
     * @return 当前租户名称
     */
    private String getCurrentTenantName() {
        if (StringUtils.isNotBlank(currentTenant.getId()) && StringUtils.isEmpty(currentTenant.getName())) {
            TenantConfiguration tenantConfiguration = tenantStore.findById(currentTenant.getId());
            return tenantConfiguration != null ? tenantConfiguration.getName() : "";
        }
        return currentTenant.getName();
    }

    /**
     * 获取指定应用及URL名称的URL，若未配置则返回null
     *
     * @param appName 应用名称
     * @param urlName URL名称（可选）
     * @return URL或null
     */
    @Override
    public String getUrlOrNull(@NotNull String appName, @Nullable String urlName) {
        ApplicationUrlMap appMap = appUrlConfiguration.getApplications();
        if (appMap == null) {
            return null;
        }
        ApplicationUrlInfo app = appMap.get(appName);
        if (app == null) {
            return null;
        }
        // 返回根URL或拼接功能URL
        if (StringUtils.isEmpty(urlName)) {
            return app.getRootUrl();
        }
        String url = app.getUrls() != null ? app.getUrls().get(urlName) : null;
        if (StringUtils.isEmpty(url)) {
            return null;
        }
        // 拼接根URL和功能URL，确保根URL以/结尾
        return ensureEndsWithSlash(app.getRootUrl()) + removeStartSlash(url);
    }

    /**
     * 确保URL以斜杠结尾
     *
     * @param url 原始URL
     * @return 以 / 结尾的URL
     */
    private String ensureEndsWithSlash(String url) {
        if (StringUtils.isEmpty(url)) {
            return "/";
        }
        return url.endsWith("/") ? url : url + "/";
    }

    /**
     * 去除URL前导斜杠，避免重复斜杠拼接
     *
     * @param url 原始URL
     * @return 无前导斜杠的URL
     */
    private String removeStartSlash(String url) {
        if (StringUtils.isEmpty(url)) {
            return "";
        }
        return url.startsWith("/") ? url.substring(1) : url;
    }
}